/* CUC Online Judge - A cross-platform judge online system
 * Base on [Verwandlung Online Judge](https://github.com/hzxie/voj)
 * Copyright (C) 2021 Jinhe Zhang <307100890@qq.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 *                             /\/`\/\
 *                            _\ | | /_
 *                          -           -
 *                        /               \
 *                       | |-------------| |
 *                       | |^=-_#   #_-=^| |
 *                       \/ \O__/,_ \O__/ \/
 *                        \  \\\     \\\  /
 *                         _\     o     /_
 *                        |/ `--=___=--` \|
 *                       /              六 \
 *                      |  ____       __七_ |
 *                       \____/_\   /_\____/
 */
package org.verwandlung.voj.web.controller;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSON;
import org.verwandlung.voj.web.exception.ResourceNotFoundException;
import org.verwandlung.voj.web.messenger.ApplicationEventListener;
import org.verwandlung.voj.web.model.Checkpoint;
import org.verwandlung.voj.web.model.ClassC;
import org.verwandlung.voj.web.model.ClassModel;
import org.verwandlung.voj.web.model.Contest;
import org.verwandlung.voj.web.model.Language;
import org.verwandlung.voj.web.model.Problem;
import org.verwandlung.voj.web.model.ProblemCategory;
import org.verwandlung.voj.web.model.ProblemTag;
import org.verwandlung.voj.web.model.Student;
import org.verwandlung.voj.web.model.User;
import org.verwandlung.voj.web.model.UserGroup;
import org.verwandlung.voj.web.service.LanguageService;
import org.verwandlung.voj.web.service.ProblemService;
import org.verwandlung.voj.web.service.StudentService;
import org.verwandlung.voj.web.service.SubmissionService;
import org.verwandlung.voj.web.service.UserService;
import org.verwandlung.voj.web.service.ClassCService;
import org.verwandlung.voj.web.service.ClassModelService;
import org.verwandlung.voj.web.service.ContestService;
import org.verwandlung.voj.web.util.DateUtils;
import org.verwandlung.voj.web.util.HttpRequestParser;
import org.verwandlung.voj.web.util.HttpSessionParser;
import org.verwandlung.voj.web.util.SessionListener;


/**
 * 用于处理教师管理系统的请求.
 * 共12个页面、14个请求.
 * 分为班级管理、试题管理、竞赛管理三大类.
 * 
 * @author Jinhe Zhang
 */
@Controller
@RequestMapping(value="/teacher")
public class TeacherController {
	/**
	 * 加载教师管理系统首页.
	 * @param request - HttpRequest对象
	 * @param response - HttpResponse对象
	 * @return 包含教师管理系统页面信息的ModelAndView对象
	 */
	@RequestMapping(value="", method=RequestMethod.GET)
	public ModelAndView indexView(HttpServletRequest request, HttpServletResponse response) {
		ModelAndView view = new ModelAndView("teacher/index");
		// zjh -- 应该做一个check，保证里面没有多余的对象
		view.addObject("totalUsers", getTotalUsers());
		view.addObject("newUsersToday", getNumberOfUserRegisteredToday());
		view.addObject("onlineUsers", getOnlineUsers());
		view.addObject("totalProblems", getTotalProblems());
		view.addObject("numberOfCheckpoints", getNumberOfCheckpoints());
		view.addObject("privateProblems", getPrivateProblems());
		view.addObject("submissionsToday", getSubmissionsToday());
		view.addObject("memoryUsage", getCurrentMemoryUsage());
		view.addObject("onlineJudgers", getOnlineJudgers());
		return view;
	}
	
	/**
	 * 获取系统一段时间内的提交次数.
	 * @param period - 时间间隔的天数
	 * @param request - HttpServletRequest对象
	 * @return 包含提交次数与时间的 Map 对象
	 */
	@RequestMapping(value="/getNumberOfSubmissions.action", method=RequestMethod.GET)
	public @ResponseBody Map<String, Object> getNumberOfSubmissionsAction(
			@RequestParam(value="period") int period,
			HttpServletRequest request) {
		// zjh -- 教师是否有必要获取提交信息是值得商榷或设计的
		Map<String, Object> submissions = new HashMap<>(2, 1);
		Date today = new Date();
		Date previousDate = DateUtils.getPreviousDate(period);
		Map<String, Long> totalSubmissions = submissionService.getNumberOfSubmissionsUsingDate(previousDate, today, 0, false);
		Map<String, Long> acceptedSubmissions = submissionService.getNumberOfSubmissionsUsingDate(previousDate, today, 0, true);
		
		submissions.put("totalSubmissions", totalSubmissions);
		submissions.put("acceptedSubmissions", acceptedSubmissions);
		return submissions;
	}

	////////////////////////////班级管理////////////////////////////
	
	/**
	 * 加载班级列表页面.
	 * @param username
	 * @param pageNumber
	 * @param keyword1
	 * @param keyword2
	 * @param langSlug
	 * @param category
	 * @param request - HttpRequest对象
	 * @param response - HttpResponse对象
	 * @return
	 */
	@RequestMapping(value="/all-classes", method=RequestMethod.GET)
	public ModelAndView allClassesView(
		@RequestParam(value="username", required=false, defaultValue="") String username,
		@RequestParam(value="page", required=false, defaultValue="1") long pageNumber,
		@RequestParam(value="keyword1", required=false, defaultValue="") String keyword1,
		@RequestParam(value="keyword2", required=false, defaultValue="") String keyword2,
		@RequestParam(value="langpre", required=false, defaultValue="") String langSlug,
		@RequestParam(value="category", required=false, defaultValue="false") Boolean category,
		HttpServletRequest request, HttpServletResponse response) {
		// zjh -- 班级列表还是应该保持分页设计
		final int NUMBER_OF_USERS_PER_PAGE = 100;
		User currentUser = HttpSessionParser.getCurrentUser(request.getSession());
		long offset = (pageNumber >= 1 ? pageNumber - 1 : 0) * NUMBER_OF_USERS_PER_PAGE;
		Language lang = languageService.getLanguageUsingSlug(langSlug);

		List<ClassC> classces = classCService.getAllClasses(keyword1, keyword2, lang, currentUser, offset, NUMBER_OF_USERS_PER_PAGE);
		long totalClasses = classCService.getCountofAllClasses(keyword1, category?null:currentUser);
		List<Language> languages = languageService.getAllLanguages();
		
		ModelAndView view = new ModelAndView("teacher/all-classes");
		view.addObject("selectedCategory", category);
		view.addObject("keyword1", keyword1);
		view.addObject("keyword2", keyword2);
		view.addObject("langSlug", langSlug);
		view.addObject("languages", languages);
		view.addObject("username", username);
		view.addObject("currentPage", pageNumber);
		view.addObject("totalPages", (long) Math.ceil(totalClasses * 1.0 / NUMBER_OF_USERS_PER_PAGE));
		view.addObject("classces",classces);
		return view;
	}
	
	/** 加载班级列表.
​     * @param request - HttpRequest对象
​     * @param response - HttpResponse对象
​     * @return 包含班级列表的ResponseBody对象
​     */
	@RequestMapping(value="/get-all-classes.action", method=RequestMethod.GET)
	public @ResponseBody List<ClassC> allClassesView(HttpServletRequest request) {
		// Map<String, Object> result = new HashMap<String, Object>(3, 1);
		List<ClassC> classces = classCService.getAllClasses("", "", null, null, 0, INF);
		return classces;
	}
	
	/**
	 * 加载班级详情列表页面.
	 * @param request - HttpRequest对象
	 * @param response - HttpResponse对象
	 * @return 包含班级详情列表页面信息的ModelAndView对象
	 */
	@RequestMapping(value="/class-details/{cid}", method=RequestMethod.GET)
	public ModelAndView ClassDetailsView(
			@PathVariable(value = "cid") Long cid,
			@RequestParam(value="userGroup", required=false, defaultValue="") String userGroupSlug,
			@RequestParam(value="username", required=false, defaultValue="") String username,
			@RequestParam(value="page", required=false, defaultValue="1") long pageNumber,
			HttpServletRequest request, HttpServletResponse response) {
		final int NUMBER_OF_STUDENTS_PER_PAGE = 6;
		long offset = (pageNumber >= 1 ? pageNumber - 1 : 0) * NUMBER_OF_STUDENTS_PER_PAGE;
		long totalStudents = studentService.getStudentCountof(cid);
		
		List<Student> students = studentService.getStudentsof(cid, offset, NUMBER_OF_STUDENTS_PER_PAGE);
		List<User> users = new ArrayList<>();
		for(Student student:students){
			users.add(studentService.getUserofStudent(student));
		}
		List<Contest> contests = classCService.getContestofClassCid(cid);
		ClassC classC = classCService.getClassof(cid);
	    
		ModelAndView view = new ModelAndView("teacher/class-details");
		view.addObject("username", username);
		view.addObject("classc", classC);
		view.addObject("currentPage", pageNumber);
		view.addObject("totalPages", (long) Math.ceil(totalStudents * 1.0 / NUMBER_OF_STUDENTS_PER_PAGE));
		view.addObject("students", students);
		view.addObject("contests", contests);
		view.addObject("users", users);
		return view;
	}
	
	/**
	 * 删除学生.
	 * @param students - 学生列表
	 * @param request - HttpServletRequest对象
	 * @return 一个包含账户创建结果的Map<String, Boolean>对象
	 */
	@RequestMapping(value="/class-details/{cid}/deleteStudent.action", method=RequestMethod.POST)
	public @ResponseBody Map<String, Boolean> deleteStudentAction(@PathVariable(value = "cid") Long cid, @RequestParam(value="students") String students, HttpServletRequest request) {
		// 删除学生需要减少总人数，需要鉴定教师是否有删除该学生的权利
		Map<String, Boolean> result = new HashMap<>(2, 1);
		String ipAddress = HttpRequestParser.getRemoteAddr(request);
		User currentUser = HttpSessionParser.getCurrentUser(request.getSession());
		ClassC classC = classCService.getClassof(cid);
		if(classC.getTeaUser().equals(currentUser)){
			result.put("isAuthorized", true);
			List<Long> studentList = JSON.parseArray(students, Long.class);
			for(Long stu_id:studentList){
				studentService.deleteStudent(stu_id);
			}
			classC.getStudentcount();
			// update
			LOGGER.info(String.format("students: [stuid=%s] was deleted by teacher[uid=%d] at %s.", new Object[] {students, currentUser.getUid(), ipAddress}));
			result.put("isSuccessful", true);
		} else {
			result.put("isSuccessful", false);
			result.put("isAuthorized", false);
		}
		return result;
	}
	

	/**
	 * 添加学生.
	 * @param students - 学生列表
	 * @param request - HttpServletRequest对象
	 * @return 一个包含账户创建结果的Map<String, Boolean>对象
	 */
	@RequestMapping(value="/class-details/{cid}/addStudent.action", method=RequestMethod.POST)
	public @ResponseBody Map<String, Boolean> addStudentAction(@PathVariable(value = "cid") Long cid, @RequestParam(value="studentName") String studentName, @RequestParam(value="studentCode") String studentCode, HttpServletRequest request) {
		// 删除学生需要减少总人数，需要鉴定教师是否有删除该学生的权利
		Map<String, Boolean> result = new HashMap<>(2, 1);
		String ipAddress = HttpRequestParser.getRemoteAddr(request);
		User currentUser = HttpSessionParser.getCurrentUser(request.getSession());
		ClassC classC = classCService.getClassof(cid);
		if(classC.getTeaUser().equals(currentUser)){
			result.put("isAuthorized", true);
			studentService.createStudent(studentName, studentCode, classC);
			classC.getStudentcount();
			// update
			LOGGER.info(String.format("student: [stucode=%s] was added by teacher[uid=%d] at %s.", new Object[] {studentCode, currentUser.getUid(), ipAddress}));
			result.put("isSuccessful", true);
		} else {
			result.put("isSuccessful", false);
			result.put("isAuthorized", false);
		}
		return result;
	}

	/**
	 * 删除班级.
	 * @param classes - 班级列表
	 * @param request - HttpServletRequest对象
	 * @return 一个包含账户创建结果的Map<String, Boolean>对象
	 */
	@RequestMapping(value="/deleteClasses.action", method=RequestMethod.POST)
	public @ResponseBody Map<String, Boolean> deleteCLassAction(@RequestParam(value="classes") String classes,
		HttpServletRequest request) {
		Map<String, Boolean> result = new HashMap<>(2, 1);
		List<Long> classList = JSON.parseArray(classes, Long.class);
		for(Long cid:classList){
			classCService.deleteClass(cid);
		}
		String ipAddress = HttpRequestParser.getRemoteAddr(request);
		LOGGER.info(String.format("class: [cid=%s] was deleted by teacher at %s.", 
				new Object[] {classList.toString(), ipAddress}));
		result.put("isSuccessful", true);
		return result;
	}
	
	/**
	 * 加载创建班级页面.
	 * @param request - HttpServletRequest对象
	 * @param response - HttpServletResponse对象
	 * @return 包含创建班级页面信息的ModelAndView对象
	 */
	@RequestMapping(value="/new-class", method=RequestMethod.GET)
	public ModelAndView newClassView(
			HttpServletRequest request, HttpServletResponse response) {
		List<Language> languages = languageService.getAllLanguages();
		List<ClassModel> classModels = classModelService.getAllClassModels();
		ModelAndView view = new ModelAndView("teacher/new-class");
		view.addObject("classModels", classModels);
		view.addObject("languages", languages);
		return view;
	}

	/**
	 * 创建新班级.
	 * @param file - 登分表
	 * @param preferLanguageSlug - 偏好语言的别名
	 * @param request - HttpServletRequest对象
	 * @return 一个包含账户创建结果的Map<String, Boolean>对象
	 */
	@RequestMapping(value="/newClass.action", method=RequestMethod.POST)
	public @ResponseBody Map<String, Boolean> newClassAction(
		@RequestParam(value="term") String term,
		@RequestParam(value="lessonId") String lessonId,
		@RequestParam(value="lessonNum") String lessonNum,
		@RequestParam(value="lessonName") String lessonName,
		@RequestParam(value="teacher") String teacher,
		@RequestParam(value="studentsCode") List<String> studentsCode,
		@RequestParam(value="studentsName") List<String> studentsName,
		@RequestParam(value="studentsClass") List<String> studentsClass,
		@RequestParam(value="languageSlug") String languageSlug,
			HttpServletRequest request, HttpServletResponse response) {
		Map<String, Boolean> result = new HashMap<String, Boolean>(3, 1);
		result.put("isSuccessful", true);
		User currentUser = HttpSessionParser.getCurrentUser(request.getSession());
		// zjh -- 这里需要根据需要对参数做校验，只是做非空判断是不够的
		if(term.isEmpty() || lessonId.isEmpty() || lessonNum.isEmpty() || lessonName.isEmpty() || teacher.isEmpty()) {
			result.put("isParamEmpty", true);
			result.replace("isSuccessful", false);
		}
		if(languageSlug.isEmpty()) {
			result.put("isLanguageIllegal", true);
			result.replace("isSuccessful", false);
		}
		if(!(studentsClass.size() == studentsCode.size() && studentsClass.size() == studentsName.size())) {
			result.put("isStudentDataIllega", true);
			result.replace("isSuccessful", false);
		}
		if(studentsClass.isEmpty()) {
			result.put("isStudentEmpty", true);
		}
		Map<String, List<Map<String, String> > > classes = new HashMap<String, List<Map<String, String> > >(3,1);
		for (int i = 0; i < studentsCode.size(); i++) {
			Map<String, String> student = new HashMap<String, String>(3,1);
			student.put("code", studentsCode.get(i));
			student.put("name", studentsName.get(i));
			if(classes.containsKey(studentsClass.get(i))){
				classes.get(studentsClass.get(i)).add(student);
			}else{
				List<Map<String, String> > firstStudent = new ArrayList<Map<String, String> >(Arrays.asList(student));
				classes.put(studentsClass.get(i), firstStudent);
			}
		}
		try{
			for(String key:classes.keySet()){
				classCService.createClass(key, lessonName, lessonId, lessonNum, teacher, classes.get(key), languageSlug, term, currentUser);
			}
		} catch (Exception e) {
			result.replace("isStudentCreateFail", true);
			result.replace("isSuccessful", false);
		}
		String ipAddress = HttpRequestParser.getRemoteAddr(request);
		LOGGER.info(String.format("Classes: [ClassName=%s] was created by teacher: [UserId=%d] at %s.", 
				new Object[] {classes.keySet().toString(), currentUser.getUid(), ipAddress}));
		return result;
	}

	////////////////////////////班级管理////////////////////////////

	////////////////////////////试题管理////////////////////////////
	
	/**
	 * 加载试题列表页面.
	 * @param request - HttpServletRequest对象
	 * @param response - HttpServletResponse对象
	 * @return 包含提交列表页面信息的ModelAndView对象
	 */
	@RequestMapping(value="/all-problems", method=RequestMethod.GET)
	public ModelAndView allProblemsView(
			@RequestParam(value="keyword", required=false, defaultValue="") String keyword,
			@RequestParam(value="problemCategory", required=false, defaultValue="") String problemCategorySlug,
			@RequestParam(value="problemTag", required=false, defaultValue="") String problemTagSlug,
			@RequestParam(value="page", required=false, defaultValue="1") long pageNumber,
			HttpServletRequest request, HttpServletResponse response) {
		final int NUMBER_OF_PROBLEMS_PER_PAGE = 100;
		List<ProblemCategory> problemCategories = problemService.getProblemCategories();
		long totalProblems = problemService.getNumberOfProblemsUsingFilters(keyword, problemCategorySlug, false);

		long offset = (pageNumber >= 1 ? pageNumber - 1 : 0) * NUMBER_OF_PROBLEMS_PER_PAGE;
		long problemIdLowerBound = problemService.getFirstIndexOfProblems() + offset;
		long problemIdUpperBound = problemIdLowerBound + NUMBER_OF_PROBLEMS_PER_PAGE - 1;

		List<Problem> problems = problemService.getProblemsUsingFilters(problemIdLowerBound, keyword, problemCategorySlug, problemTagSlug, false, NUMBER_OF_PROBLEMS_PER_PAGE);
		Map<Long, List<ProblemCategory>> problemCategoryRelationships =
				problemService.getProblemCategoriesOfProblems(problemIdLowerBound, problemIdUpperBound);
		Map<Long, List<ProblemTag>> problemTagRelationships =
				problemService.getProblemTagsOfProblems(problemIdLowerBound, problemIdUpperBound);

		ModelAndView view = new ModelAndView("teacher/all-problems");
		view.addObject("problemCategories", problemCategories);
		view.addObject("selectedProblemCategory", problemCategorySlug);
		view.addObject("keyword", keyword);
		view.addObject("currentPage", pageNumber);
		view.addObject("totalPages", (long) Math.ceil(totalProblems * 1.0 / NUMBER_OF_PROBLEMS_PER_PAGE));
		view.addObject("problems", problems);
		view.addObject("problemCategoryRelationships", problemCategoryRelationships);
		view.addObject("problemTagRelationships", problemTagRelationships);
		return view;
	}

	/** 删除选定的试题.
	 * @param problems - 试题ID的集合, 以逗号(, )分隔
	 * @param request - HttpServletRequest对象
	 * @return 试题的删除结果
	 */
	@RequestMapping(value="/deleteProblems.action", method=RequestMethod.POST)
	public @ResponseBody Map<String, Boolean> deleteProblemsAction(
			@RequestParam(value="problems") String problems,
			HttpServletRequest request) {
		// zjh -- 教师是否应该具备删除试题的能力是值得商榷的
		// zjh -- 试题被删除会导致竞赛取不到试题
		Map<String, Boolean> result = new HashMap<>(2, 1);
		List<Long> problemList = JSON.parseArray(problems, Long.class);
		
		for ( Long problemId : problemList ) {
			problemService.deleteProblem(problemId);
			
			String ipAddress = HttpRequestParser.getRemoteAddr(request);
			LOGGER.info(String.format("Problem: [ProblemId=%s] was deleted by teacher at %s.", 
					new Object[] {problemId, ipAddress}));
		}
		result.put("isSuccessful", true);
		return result;
	}
	
	/** 加载创建试题页面.
	 * @param request - HttpServletRequest对象
	 * @param response - HttpServletResponse对象
	 * @return 包含创建试题页面信息的ModelAndView对象
	 */
	@RequestMapping(value="/new-problem", method=RequestMethod.GET)
	public ModelAndView newProblemView(
			HttpServletRequest request, HttpServletResponse response) {
		Map<ProblemCategory, List<ProblemCategory>> problemCategories = problemService.getProblemCategoriesWithHierarchy();
		
		ModelAndView view = new ModelAndView("teacher/new-problem");
		view.addObject("problemCategories", problemCategories);
		return view;
	}


	/** 处理用户创建试题的请求.
	 * @param problemName - 试题名称
	 * @param timeLimit - 时间限制
	 * @param memoryLimit - 内存占用限制
	 * @param description - 试题描述
	 * @param hint - 试题提示
	 * @param inputFormat - 输入格式
	 * @param outputFormat - 输出格式
	 * @param inputSample - 输入样例
	 * @param outputSample - 输出样例
	 * @param testCases - 测试用例(JSON 格式)
	 * @param problemCategories - 试题分类(JSON 格式)
	 * @param problemTags - 试题标签((JSON 格式)
	 * @param isPublic - 试题是否公开
	 * @param isExactlyMatch - 测试点是否精确匹配
	 * @param request - HttpServletRequest对象
	 * @return 包含试题创建结果的 Map<String, Boolean>对象
	 */
	@RequestMapping(value="/createProblem.action", method=RequestMethod.POST)
	public @ResponseBody Map<String, Object> createProblemAction(
			@RequestParam(value="problemName") String problemName,
			@RequestParam(value="timeLimit") String timeLimit, 
			@RequestParam(value="memoryLimit") String memoryLimit, 
			@RequestParam(value="description") String description, 
			@RequestParam(value="hint") String hint, 
			@RequestParam(value="inputFormat") String inputFormat, 
			@RequestParam(value="outputFormat") String outputFormat, 
			@RequestParam(value="inputSample") String inputSample, 
			@RequestParam(value="outputSample") String outputSample, 
			@RequestParam(value="testCases") String testCases, 
			@RequestParam(value="problemCategories") String problemCategories, 
			@RequestParam(value="problemTags") String problemTags, 
			@RequestParam(value="isPublic") boolean isPublic, 
			@RequestParam(value="isExactlyMatch") boolean isExactlyMatch,
			HttpServletRequest request) {
		if ( timeLimit.isEmpty() || !StringUtils.isNumeric(timeLimit) ) {
			timeLimit = "-1";
		}
		if ( memoryLimit.isEmpty() || !StringUtils.isNumeric(memoryLimit) ) {
			memoryLimit = "-1";
		}
		Map<String, Object> result = problemService.createProblem(problemName, Integer.parseInt(timeLimit), 
				Integer.parseInt(memoryLimit), description, hint, inputFormat, outputFormat, inputSample, 
				outputSample, testCases, problemCategories, problemTags, isPublic, isExactlyMatch);
		
		if ( (boolean) result.get("isSuccessful") ) {
			long problemId = (Long) result.get("problemId");
			String ipAddress = HttpRequestParser.getRemoteAddr(request);
			
			LOGGER.info(String.format("Problem: [ProblemId=%s] was created by teacher at %s.", 
					new Object[] {problemId, ipAddress}));
		}
		return result;
	}

	/** 加载批量创建试题页面.
	 * @param request - HttpServletRequest对象
	 * @param response - HttpServletResponse对象
	 * @return 包含批量创建试题页面信息的ModelAndView对象
	 */
	@RequestMapping(value="/new-problems", method=RequestMethod.GET)
	public ModelAndView newProblemsView(
			HttpServletRequest request, HttpServletResponse response) {
		Map<ProblemCategory, List<ProblemCategory>> problemCategories = problemService.getProblemCategoriesWithHierarchy();
		
		ModelAndView view = new ModelAndView("teacher/new-problems");
		view.addObject("problemCategories", problemCategories);
		return view;
	}
	
	/** 加载编辑试题页面.
	 * @param problemId - 试题的唯一标识符
	 * @param request - HttpServletRequest对象
	 * @param response - HttpServletResponse对象
	 * @return 包含提交列表页面信息的ModelAndView对象
	 */
	@RequestMapping(value="/edit-problem/{problemId}", method=RequestMethod.GET)
	public ModelAndView editProblemsView(
			@PathVariable(value = "problemId") long problemId,
			HttpServletRequest request, HttpServletResponse response) {
		Problem problem = problemService.getProblem(problemId);
		
		if ( problem == null ) {
			throw new ResourceNotFoundException();
		}
		List<Checkpoint> checkpoints = problemService.getCheckpointsUsingProblemId(problemId);
		List<ProblemCategory> selectedProblemCategories = problemService.getProblemCategoriesUsingProblemId(problemId);
		Map<ProblemCategory, List<ProblemCategory>> problemCategories = problemService.getProblemCategoriesWithHierarchy();
		List<ProblemTag> problemTags = problemService.getProblemTagsUsingProblemId(problemId);
		
		ModelAndView view = new ModelAndView("teacher/edit-problem");
		view.addObject("problem", problem);
		view.addObject("checkpoints", checkpoints);
		view.addObject("problemCategories", problemCategories);
		view.addObject("selectedProblemCategories", selectedProblemCategories);
		view.addObject("problemTags", problemTags);
		return view;
	}
	
	/** 处理用户编辑试题的请求.
	 * @param problemName - 试题名称
	 * @param timeLimit - 时间限制
	 * @param memoryLimit - 内存占用限制
	 * @param description - 试题描述
	 * @param hint - 试题提示
	 * @param inputFormat - 输入格式
	 * @param outputFormat - 输出格式
	 * @param inputSample - 输入样例
	 * @param outputSample - 输出样例
	 * @param testCases - 测试用例(JSON 格式)
	 * @param problemCategories - 试题分类(JSON 格式)
	 * @param problemTags - 试题标签((JSON 格式)
	 * @param isPublic - 试题是否公开
	 * @param isExactlyMatch - 测试点是否精确匹配
	 * @param request - HttpServletRequest对象
	 * @return 包含试题编辑结果的 Map<String, Boolean>对象
	 */
	@RequestMapping(value="/editProblem.action", method=RequestMethod.POST)
	public @ResponseBody Map<String, Boolean> editProblemAction(
			@RequestParam(value="problemId") long problemId,
			@RequestParam(value="problemName") String problemName,
			@RequestParam(value="timeLimit") String timeLimit, 
			@RequestParam(value="memoryLimit") String memoryLimit, 
			@RequestParam(value="description") String description, 
			@RequestParam(value="hint") String hint, 
			@RequestParam(value="inputFormat") String inputFormat, 
			@RequestParam(value="outputFormat") String outputFormat, 
			@RequestParam(value="inputSample") String inputSample, 
			@RequestParam(value="outputSample") String outputSample, 
			@RequestParam(value="testCases") String testCases, 
			@RequestParam(value="problemCategories") String problemCategories, 
			@RequestParam(value="problemTags") String problemTags, 
			@RequestParam(value="isPublic") boolean isPublic, 
			@RequestParam(value="isExactlyMatch") boolean isExactlyMatch,
			HttpServletRequest request) {
		if ( timeLimit.isEmpty() || !StringUtils.isNumeric(timeLimit) ) {
			timeLimit = "-1";
		}
		if ( memoryLimit.isEmpty() || !StringUtils.isNumeric(memoryLimit) ) {
			memoryLimit = "-1";
		}
		Map<String, Boolean> result = problemService.editProblem(problemId, problemName, Integer.parseInt(timeLimit), 
				Integer.parseInt(memoryLimit), description, hint, inputFormat, outputFormat, inputSample, 
				outputSample, testCases, problemCategories, problemTags, isPublic, isExactlyMatch);
		
		if ( result.get("isSuccessful") ) {
			String ipAddress = HttpRequestParser.getRemoteAddr(request);
			
			LOGGER.info(String.format("Problem: [ProblemId=%s] was edited by teacher at %s.", 
					new Object[] {problemId, ipAddress}));
		}
		return result;
	}
	

	@RequestMapping(value="/problem/{problemId}", method=RequestMethod.GET)
	public @ResponseBody Map<String, Object> getProblem(
		@PathVariable("problemId") long problemId,
		HttpServletRequest request) {
			Map<String, Object> result = new HashMap<>(4, 1);
			Problem problem = problemService.getProblem(problemId);
			if(problem != null) {
				result.put("problemName", problem.getProblemName());
				result.put("problemTimeLimit", problem.getTimeLimit());
				result.put("problemMemoryLimit", problem.getMemoryLimit());
				result.put("result", "success");
			} else {
				result.put("result", "fail");
			}
			return result;
	}

	// zjh -- 试题分类部分的使用是需要商榷的

	/** 加载试题分类页面.
	 * @param request - HttpServletRequest对象
	 * @param response - HttpServletResponse对象
	 * @return 包含试题分类页面信息的ModelAndView对象.
	 */
	@RequestMapping(value="/problem-categories", method=RequestMethod.GET)
	public ModelAndView problemCategoriesView(
			HttpServletRequest request, HttpServletResponse response) {
		List<ProblemCategory> problemCategories = problemService.getProblemCategories();
		
		ModelAndView view = new ModelAndView("teacher/problem-categories");
		view.addObject("problemCategories", problemCategories);
		return view;
	}
	
	/** 创建试题分类.
	 * @param problemCategorySlug - 试题分类的别名
	 * @param problemCategoryName - 试题分类的名称
	 * @param parentProblemCategorySlug - 父级试题分类的别名
	 * @param request - HttpServletRequest对象
	 * @return 包含试题分类的创建结果的Map<String, Object>对象
	 */
	@RequestMapping(value="/createProblemCategory.action", method=RequestMethod.POST)
	public @ResponseBody Map<String, Object> createProblemCategoryAction(
			@RequestParam(value="problemCategorySlug") String problemCategorySlug,
			@RequestParam(value="problemCategoryName") String problemCategoryName,
			@RequestParam(value="parentProblemCategory") String parentProblemCategorySlug,
			HttpServletRequest request) {
		Map<String, Object> result = problemService.createProblemCategory(
				problemCategorySlug, problemCategoryName, parentProblemCategorySlug);
		
		if ( (boolean) result.get("isSuccessful") ) {
			long problemCategoryId = (Long) result.get("problemCategoryId");
			String ipAddress = HttpRequestParser.getRemoteAddr(request);
			
			LOGGER.info(String.format("ProblemCategory: [ProblemCategoryId=%s] was created by teacher at %s.", 
					new Object[] {problemCategoryId, ipAddress}));
		}
		return result;
	}
	
	/** 编辑试题分类.
	 * @param problemCategoryId - 试题分类的唯一标识符
	 * @param problemCategorySlug - 试题分类的别名
	 * @param problemCategoryName - 试题分类的名称
	 * @param parentProblemCategorySlug - 父级试题分类的别名
	 * @param request - HttpServletRequest对象
	 * @return 包含试题分类的编辑结果的Map<String, Boolean>对象
	 */
	@RequestMapping(value="/editProblemCategory.action", method=RequestMethod.POST)
	public @ResponseBody Map<String, Boolean> editProblemCategoryAction(
			@RequestParam(value="problemCategoryId") String problemCategoryId,
			@RequestParam(value="problemCategorySlug") String problemCategorySlug,
			@RequestParam(value="problemCategoryName") String problemCategoryName,
			@RequestParam(value="parentProblemCategory") String parentProblemCategorySlug,
			HttpServletRequest request) {
		Map<String, Boolean> result = problemService.editProblemCategory(
				Integer.parseInt(problemCategoryId), problemCategorySlug, 
				problemCategoryName, parentProblemCategorySlug);

		if ( result.get("isSuccessful") ) {
			String ipAddress = HttpRequestParser.getRemoteAddr(request);

			LOGGER.info(String.format("ProblemCategory: [ProblemCategoryId=%s] was edited by teacher at %s.",
					new Object[] {problemCategoryId, ipAddress}));
		}
		return result;
	}

	/** 删除试题分类.
	 * @param problemCategories - 试题分类的唯一标识符集合
	 * @param request - HttpServletRequest对象
	 * @return 包含试题分类的删除结果的Map<String, Boolean>对象
	 */
	@RequestMapping(value="/deleteProblemCategories.action", method=RequestMethod.POST)
	public @ResponseBody Map<String, Object> deleteProblemCategoryAction(
			@RequestParam(value="problemCategories") String problemCategories,
			HttpServletRequest request) {
		Map<String, Object> result = new HashMap<>(3, 1);
		List<Integer> problemCategoryList = JSON.parseArray(problemCategories, Integer.class);
		List<Integer> deletedProblemCategories = new ArrayList<>();

		for ( int problemCategoryId : problemCategoryList ) {
			if ( problemService.deleteProblemCategory(problemCategoryId) ) {
				deletedProblemCategories.add(problemCategoryId);
			}
			String ipAddress = HttpRequestParser.getRemoteAddr(request);
			LOGGER.info(String.format("ProblemCategory: [ProblemCategoryId=%s] was deleted by teacher at %s.",
					new Object[] {problemCategoryId, ipAddress}));
		}
		result.put("isSuccessful", true);
		result.put("deletedProblemCategories", deletedProblemCategories);
		return result;
	}
	
	////////////////////////////试题管理////////////////////////////

	////////////////////////////竞赛管理////////////////////////////

	/** 加载竞赛列表页面.
	 * @param keyword - 关键词
	 * @param username - 竞赛管理者的用户名
	 * @param page - 当前页面的页码
	 * @param request - HttpServletRequest对象
	 * @param response - HttpServletResponse对象
	 * @return 包含竞赛列表页面信息的ModelAndView对象
	 */
	@RequestMapping(value="/all-contests", method=RequestMethod.GET)
	public ModelAndView allContestsView(
			@RequestParam(value="keyword", required=false, defaultValue="") String keyword,
			@RequestParam(value="category", required=false, defaultValue="false") boolean category,
			@RequestParam(value="username", required=false, defaultValue="") String username,
			@RequestParam(value="page", required=false, defaultValue="1") long pageNumber,
			HttpServletRequest request, HttpServletResponse response) {
		final int NUMBER_OF_CONTESTS_PER_PAGE = 100;
		User currentUser = HttpSessionParser.getCurrentUser(request.getSession());
		long totalContests = contestService.getNumberOfContests(keyword, category?currentUser:null);
		List<Contest> contests = contestService.getContests(keyword, category?currentUser:null, 0, NUMBER_OF_CONTESTS_PER_PAGE);
		
		ModelAndView view = new ModelAndView("teacher/all-contests");
		view.addObject("keyword", keyword);
		view.addObject("currentPage", pageNumber);
		view.addObject("selectedCategory", category);
		view.addObject("totalPages", (long) Math.ceil(totalContests * 1.0 / NUMBER_OF_CONTESTS_PER_PAGE));
		view.addObject("contests", contests);
		view.addObject("currentTime", new Date());
		view.addObject("username", username);
		return view;
	}
	
	/** 加载竞赛编辑页面.
	 * @param contestId - 竞赛的唯一标识符
	 * @param username - 提交者的用户名
	 * @param pageNumber - 当前页面的页码
	 * @param request - HttpServletRequest对象
	 * @param response - HttpServletResponse对象
	 * @return 包含竞赛编辑页面信息的ModelAndView对象
	 */
	@RequestMapping(value="/edit-contest/{contestId}", method=RequestMethod.GET)
	public ModelAndView editContestsView(
			@PathVariable(value = "contestId") long contestId,
			@RequestParam(value="keyword", required=false, defaultValue="") String keyword,
			@RequestParam(value="username", required=false, defaultValue="") String username,
			@RequestParam(value="page", required=false, defaultValue="1") long pageNumber,
			HttpServletRequest request, HttpServletResponse response) {
		User currentUser = HttpSessionParser.getCurrentUser(request.getSession());
		Contest contest = contestService.getContest(contestId);
		if(!contest.getContestEditPublic() && !contest.getAuthor().equals(currentUser) || contestService.getContestStatus(contest) != Contest.CONTEST_STATUS.READY){
			throw new ResourceNotFoundException();
		}
		List<Long> problemIdList = JSON.parseArray(contest.getProblems(), Long.class);
		List<Long> classIdList = JSON.parseArray(contest.getClasses(), Long.class);
		List<ClassC> classes = classCService.getAllClasses("","",null,null,0,INF);
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String startTime = sdf.format(contest.getStartTime());
		String endTime = sdf.format(contest.getEndTime());
		ModelAndView view = new ModelAndView("teacher/edit-contest");
		view.addObject("contest", contest);
		view.addObject("contestStartTime", startTime);
		view.addObject("contestEndTime", endTime);
		view.addObject("problemIdList", problemIdList);
		view.addObject("classIdList", classIdList);
		view.addObject("classes", classes);
		view.addObject("username", username);
		return view;
	}
	
	/** 加载提交列表页面.
	 * @param problemId - 提交对应试题的唯一标识符
	 * @param username - 提交者的用户名
	 * @param pageNumber - 当前页面的页码
	 * @param request - HttpServletRequest对象
	 * @param response - HttpServletResponse对象
	 * @return 包含提交列表页面信息的ModelAndView对象
	 */
	@RequestMapping(value="/edit-contest/{contestId}/editContest.action", method=RequestMethod.POST)
	public @ResponseBody Map<String, Boolean> changeProblemofContest(
		@PathVariable(value = "contestId") long contestId,
		@RequestParam(value = "contestName") String contestName,
		@RequestParam(value = "starttime") String starttime,
		@RequestParam(value = "endtime") String endtime,
		@RequestParam(value = "contestMode") String contestMode,
		@RequestParam(value = "contestNote") String contestNote,
		@RequestParam(value = "contestPublic") Boolean contestPublic,
		@RequestParam(value = "contestEditPublic") Boolean contestEditPublic,
		@RequestParam(value = "problems") String problems,
		@RequestParam(value = "classes") String classes,
		HttpServletRequest request, HttpServletResponse response) {
		// zjh -- 这里应该对非对应权限做拦截
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date startTime = null, endTime = null;
		try {
			startTime = sdf.parse(starttime);
			endTime = sdf.parse(endtime);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		Map<String, Boolean> result = new HashMap<>(3, 1);
		String ipAddress = HttpRequestParser.getRemoteAddr(request);

		User currentUser = HttpSessionParser.getCurrentUser(request.getSession());

		result = contestService.editContest(contestId, contestName, contestNote, startTime, endTime, contestMode, contestPublic, contestEditPublic, problems, classes, currentUser);
		
		LOGGER.info(String.format("Contest: [ContestId=%s] change by Teacher at %s.", new Object[] {contestId, contestId, ipAddress}));

		result.put("isSuccessful", true);
		return result;
	}
	
	/** 加载创建竞赛页面.
	 * @param request - HttpServletRequest对象
	 * @param response - HttpServletResponse对象
	 * @return 包含创建试题页面信息的ModelAndView对象
	 */
	@RequestMapping(value="/new-contest", method=RequestMethod.GET)
	public ModelAndView newContestView(
			HttpServletRequest request, HttpServletResponse response) {
		ModelAndView view = new ModelAndView("teacher/new-contest");
		return view;
	}

	@RequestMapping(value="/createContest.action", method=RequestMethod.POST)
	public @ResponseBody Map<String, Object> createContest(
		@RequestParam(value = "contestName") String contestName,
		@RequestParam(value = "starttime") String starttime,
		@RequestParam(value = "endtime") String endtime,
		@RequestParam(value = "contestMode") String contestMode,
		@RequestParam(value = "contestNote") String contestNote,
		@RequestParam(value = "contestPublic") Boolean contestPublic,
		@RequestParam(value = "contestEditPublic") Boolean contestEditPublic,
		@RequestParam(value = "problems") String problems,
		@RequestParam(value = "classes") String classes,
		HttpServletRequest request, HttpServletResponse response) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date startTime = null, endTime = null;
		try {
			startTime = sdf.parse(starttime);
			endTime = sdf.parse(endtime);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		// LOGGER.info("231kh3kjeoayd"+problems+classes);
		Map<String, Object> result = new HashMap<>(3, 1);

		User currentUser = HttpSessionParser.getCurrentUser(request.getSession());
		Contest contest = new Contest(contestName, contestNote, startTime, endTime, contestMode, contestPublic, contestEditPublic, problems, classes, currentUser);
		contestService.createContest(contest);
		Long contestId = contest.getContestId();
		List<Long> classList = JSON.parseArray(classes, Long.class);
		for(long cid:classList){
			List<Student> students = studentService.getStudentsof(cid, 0, INF);
			for(Student student:students){
				User user = studentService.getUserofStudent(student);
				Map<String, Boolean> res = contestService.attendContest(contestId, user, true);
				LOGGER.info(String.format("User: [[UId=%d] attended contest: [contestId=%d] with info: [%s]", new Object[] {user.getUid(), contestId, res}));
			}
		}
		/* for (int i = 0; i < studentList.length; i++) {
			String temp = "c" + contestId + "_" + studentList.get(i).getStudentCode();
			userService.createUser(temp, "cucoj123", temp + "@cuc.edu.cn", userGroupSlug, languageSlug);
		} */
		String ipAddress = HttpRequestParser.getRemoteAddr(request);
		LOGGER.info(String.format("Contest: [ContestId=%s] was created by teacher at %s", new Object[] {contestId,  ipAddress}));
		result.put("contestId", contestId);
		result.put("isSuccessful", true);
		return result;
	}

	////////////////////////////竞赛管理////////////////////////////

	/**
	 * 获取系统中注册用户的总数.
	 * @return 系统中注册用户的总数
	 */
	private long getTotalUsers() {
		UserGroup userGroup = userService.getUserGroupUsingSlug("users");
		return userService.getNumberOfUsers(userGroup);
	}
	
	/**
	 * 获取今日注册的用户数量.
	 * @return 今日注册的用户数量
	 */
	public long getNumberOfUserRegisteredToday() {
		return userService.getNumberOfUserRegisteredToday();
	}
	
	/**
	 * 获取在线用户的数量.
	 * @return 在线用户的数量
	 */
	private long getOnlineUsers() {
		return SessionListener.getTotalSessions();
	}
	
	/**
	 * 获取全部试题的总数量.
	 * @return 全部试题的总数量
	 */
	private long getTotalProblems() {
		return problemService.getNumberOfProblems();
	}
	
	/**
	 * 获取私有试题的数量.
	 * @return 私有试题的数量
	 */
	private long getPrivateProblems() {
		long numberOfTotalProblems = getTotalProblems();
		long numberOfPublicProblems = problemService.getNumberOfProblemsUsingFilters(null, "", true);
		return numberOfTotalProblems - numberOfPublicProblems;
	}
	
	/**
	 * 获取全部试题测试点的数量(包括私有试题).
	 * @return 全部试题测试点的数量
	 */
	private long getNumberOfCheckpoints() {
		return problemService.getNumberOfCheckpoints();
	}

	/**
	 * 获取今日的提交数量.
	 * @return 今日的提交数量
	 */
	private long getSubmissionsToday() {
		Calendar calendar = Calendar.getInstance();
		int year = calendar.get(Calendar.YEAR);
		int month = calendar.get(Calendar.MONTH);
		int date = calendar.get(Calendar.DAY_OF_MONTH);
		
		calendar.set(year, month, date, 0, 0, 0);
		Date startTime = calendar.getTime();
		
		calendar.set(year, month, date + 1, 0, 0, 0);
		Date endTime = calendar.getTime();
		return submissionService.getNumberOfSubmissionsUsingDate(startTime, endTime);
	}
	
	/**
	 * 获取Web应用当前内存占用情况.
	 * @return Web应用当前内存占用(MB)
	 */
	private long getCurrentMemoryUsage() {
		long totalMemory = Runtime.getRuntime().totalMemory();
		long freeMemory = Runtime.getRuntime().freeMemory();
		
		return (totalMemory - freeMemory) / 1048576;
	}
	
	/**
	 * 获取在线的评测机数量.
	 * 通过获取监听消息队列的Consumer数量.
	 * @return 在线的评测机数量
	 */
	private long getOnlineJudgers() {
		return eventListener.getOnlineJudgers();
	}
	
	/**
	 * 自动注入的UserService对象.
	 */
	@Autowired
	private UserService userService;

	/**
	 * 自动注入的UserService对象.
	 */
	@Autowired
	private ClassCService classCService;
	/**
	/**
	 * 自动注入的UserService对象.
	 */
	@Autowired
	private ClassModelService classModelService;
	/**
	 * 自动注入的UserService对象.
	 */
	@Autowired
	private StudentService studentService;

	@Autowired
	private ContestService contestService;
	
	/**
	 * 自动注入的ProblemService对象.
	 * 用于获取试题记录信息.
	 */
	@Autowired
	private ProblemService problemService;
	
	/**
	 * 自动注入的SubmissionService对象.
	 * 用于获取提交记录信息.
	 */
	@Autowired
	private SubmissionService submissionService;
	
	/**
	 * 自动注入的LanguageService对象.
	 * 用于获取系统中的编程语言选项.
	 */
	@Autowired
	private LanguageService languageService;
	
	/**
	 * 自动注入的ApplicationEventListener对象.
	 * 用于获取在线评测机的数量.
	 */
	@Autowired
	private ApplicationEventListener eventListener;
	
	/**
	 * 日志记录器.
	 */
	private static final Logger LOGGER = LogManager.getLogger(TeacherController.class);

	private static final int INF = Integer.MAX_VALUE;
}
